前情提要:
昨天介紹了在Parcel中如何使用CSS、PostCSS(Autoprefixer)和SCSS。
今天我們就過去前幾天的經驗做個todos練習吧!
昨天找了很多大家的作品,也曾經想過自己找個api抓抓資料render過濾,最後想著果然還是應該要有畫面才能傳達練習的內容,所以我們簡單就好XD,畢竟今天是重陽節...
欸?什麼跟什麼?有什麼關聯嗎?重陽節很忙,當然不是登高望遠,反正就忙碌重陽節QAQ
操作上參照的雛形是這個:todos
預計功能會有:
那麼就來動工吧!
首先,我們先處理input的部分
class NewTodo extends Component { // 1.
constructor(props) { // 2.
super(props);
this.state = {
todoVal: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) { // 3.
this.setState({
todoVal: e.target.value
});
}
handleSubmit(e) { // 4. props的function來自父層<App/>
e.preventDefault();
if(this.state.todoVal) { // 5. 判斷todoVal是否有值
this.props.addNewTodo(this.state.todoVal);
this.setState({ todoVal: "" }); // 6. 重置input
}
}
render() {
return (
<form onSubmit={this.handleSubmit}> { /* 4. onsubmit event */ }
<input
type="text"
value={this.state.todoVal}
onChange={this.handleChange} { /* 3. onchange event */ }
/>
</form>
);
}
}
export default NewTodo;
而在index.js這邊:
import NewTodo from './src/components/new_todo'; // 1. 匯入NewTodo
class App extends Component {
constructor(props) {
super(props);
this.state = { // 2. 初始化state,todoList會集合我們在input輸入的value
todoList: []
}
this.addNewTodo = this.addNewTodo.bind(this);
}
addNewTodo(text) { // 3. text的value來自NewTodo
const newItem = { // 4. todoList array的每個element內容
text,
key: uuid(),
completed: false
}
this.setState({ // 5. 更新todoList
todoList: [...this.state.todoList, newItem]
})
}
render() {
return (
<div>
<NewTodo addNewTodo={this.addNewTodo}/>
</div>
);
}
}
接下來處理顯示部份,新增一個component命名為todo_list.js。
在todo_list裡,我只需要做三件事
import React from 'react';
const TodoList = ({todo, completeTodo, removeTodo}) => ( // 解構props
todo.map((item, index) => ( { /* 1. render todoList的內容 */ }
<li key={item.key}>
<input { /* 2. checkbox標記待辦事項是否完成 */ }
type="checkbox"
checked={item.completed}
onChange={() => completeTodo(index, { ...item, completed: !item.completed})}
/>
<span>{item.text}</span>
{ /* 3. 點擊button移除該項待辦事項▼ */ }
<button onClick={() => removeTodo(index)}>移除</button>
</li>
))
)
export default TodoList;
剛剛可以看到todo_list做的事都很簡單,因為已經把燙手山芋交給爸爸了(父層component),接下來我們來處理燙手山芋吧!
import TodoList from './src/components/todo_list'; // 1. 匯入TodoList
constructor(props) {
super(props);
// ...
this.completeTodo = this.completeTodo.bind(this);
this.removeTodo = this.removeTodo.bind(this);
}
// ...
completeTodo(index, item) { // 2-2
const todoList = update(this.state.todoList, {
[index]: {$set: item}
});
this.setState({ todoList })
}
removeTodo(index) { // 2-3
const todoList = update(this.state.todoList, {
$splice: [[index, 1]]
});
this.setState({ todoList });
}
render() {
const todoList = this.state.todoList;
const hasTodoList = todoList.length > 0;
return (
...
{ hasTodoList && ( { /* 2. */ }
<ul className="list">
<TodoList
todo={todoList} { /* 2-1 */ }
completeTodo={this.completeTodo} { /* 2-2 */ }
removeTodo={this.removeTodo} { /* 2-3 */ }
/>
</ul>
)}
...
)
}
React官方建議把state當作是不可變的data(相關內容可參考The Power Of Not Mutating Data,為了保持state的不可變性,在這邊想了很多。我們之前在處理state的時候幾乎只用到string或boolean,這兩個型別很單純,一但新值更新就直接覆蓋,但如果state存的是一個Object,很容易讓state資料產生變異。
React提出幾個解套方案Using Immutable Data Structures,我在這邊用了immutability-helper
使用記得先安裝yarn add immutability-helper
,然後匯入module
import update from 'immutability-helper';
另外,在這個階段發現另一個問題,本來打算將key值設為array的index。(又在偷懶!)
但發現之後新增/移除時變動資料的話,array的index會因為增減受影響,所以這裡安裝node-uuid使用uuid(Universally Unique IDentifier,通用唯一識別碼),避免造成未來的煩惱。使用前一樣先安裝yarn add uuid
,然後更新index.js內的addNewTodo function
import uuid from 'uuid/v4';
// addNewTodo 調整成
addNewTodo(text) {
const newItem = {
text,
key: uuid(),
completed: false
}
this.setState({
todoList: [...this.state.todoList, newItem]
})
}
在自己寫react的時候,需要常常查看props和state的值是否正確,React Developer Tools可以讓我們在開發的時候隨時監控state和props的變化。
安裝完後會在chrome的devtools看到react的tab,就可以開始使用囉。
在設定那邊可以勾選Highlight Update
測試結果如下:
可以看出,新增項目的時候在更新,勾選項目的時候有更新,刪除項目的時候也更新。
等等!好像不太對,新增項目為什麼時時刻刻跟著其他人一起更新,明明勾選和刪除沒有他的事,原來是因為父層component的資料更新了,所以作為子層的NewTodo也一起收到變動而re-render。但這邊他只需要維持現狀就好,所以回想過去幾天在lifecycle那邊提到的shouldComponentUpdate
不應該使用它防止渲染(render),所以我們使用PureComponent
處理吧!操作結果如下:
調整後的NewTodo就不會受影響re-render了!
加上css調整後,最後操作如下:
github傳送門
今日總結:
實作之後可以得到不同的收穫,就算題目多麼的小XD
本日到這告一段論,大家重陽節快樂~